#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include "Log_Client.h"

#ifdef USE_TCP_FLAG
    #include "TCPInterface.h"
#else
    #include "UDPInterface.h"
#endif


// Client 应该负责，查询日志，添加日志，删除日志的接口
// Server 负责日志的记录，和sqlite3交互

CLogClient g_clientObj;


static const char* g_LogLevelArr[MAX_LOG_LEVEL] = {
    "DEBUG",
    "INFO",
    "WARN",
    "ERROR",
    "FATAL"
};


static std::string GetLogLevelStr(LOG_LEVEL nLogLevel)
{
    if ( (nLogLevel > MAX_LOG_LEVEL) || (nLogLevel < 0) )
    {
        return "DEBUG";
    }

    return g_LogLevelArr[nLogLevel];
}

static LOG_LEVEL GetLogLevelEnum(const std::string &strLogLevel)
{
    if (strLogLevel.length() == 0)
    {
        return DEBUG_LEVEL;
    }
    // toUpper
    std::string strUpperLogLevel = "";
    for (char c: strLogLevel)
    {
        strUpperLogLevel += toupper(c);
    }

    int nIndex;
    for (nIndex = 0; nIndex< MAX_LOG_LEVEL; nIndex++)
    {
        std::string strData = g_LogLevelArr[nIndex];
        if(strUpperLogLevel == strData)
        {
            return (LOG_LEVEL)nIndex;
        }
    }

    return DEBUG_LEVEL;

}


int AddLog(LOG_LEVEL nAddLogLevel, const std::string &strSource, const char* logInfo, ...)
{
    if(!g_clientObj.IsLogClientInit())
    {
        if(g_clientObj.InitLogClient() == false)
        {
            printf("InitLogClient() false\n");
            return -1;
        }
    }

	g_clientObj.LockMutex();
	
	
    // prepare Data
    std::string strLogText;

    char cstrLogBuff[1024];
    va_list valist;
    va_start(valist, logInfo);
    vsnprintf(cstrLogBuff, sizeof(cstrLogBuff), logInfo,valist);
    va_end(valist);

    strLogText = cstrLogBuff;


    /* Priority is only valid if it is greater than the set value */
    std::string strAddLogLevel = GetLogLevelStr(nAddLogLevel);
    std::string strSettingLevel;
    LOG_LEVEL nSettingLevel = GetLogLevelEnum(strSettingLevel);


    if (nAddLogLevel < nSettingLevel)
    {
        printf("ERROR || Unable to add log, nAddLogLevel = %s(%d), nSettingLevel = %s(%d)\n", 
                        strAddLogLevel.c_str(), nAddLogLevel, strSettingLevel.c_str(), nSettingLevel);

        g_clientObj.UnLockMutex();
        return -1;
    }


    /* If Debug mode is set, all logs must be printed to the serial port */
    std::string strDebugMode;
    if (strDebugMode == "true")
    {
        // [LogLevel] | [Module Name] | Detail Info
        printf(" ------ [%s] | [%s] | %s\n", strAddLogLevel.c_str(), strSource.c_str(), strLogText.c_str());
    }


    LOG_COMM_STU commuStu;

    // Build Messate
    CLogClient::AddLogBuildMessage(nAddLogLevel, strSource, strLogText, commuStu);

    // Send to Server
    g_clientObj.SendMessageToServer(commuStu);

    std::string strRspData;
    // Get the Response
    if (g_clientObj.GetRespondFromServer(strRspData) == false)
    {
        printf("Fail to get response from server\n");
    }

    g_clientObj.UnLockMutex();

    return 0;
}


int QueryLog(int nLogCount, const std::string strKeyWord, std::string &strResult)
{
    if (nLogCount < 0)
    {
        nLogCount = 0;
    }

    strResult = "Execute Fail in QueryLog()";
    if(!g_clientObj.IsLogClientInit())
    {
        if(g_clientObj.InitLogClient() == false)
        {
            printf("InitLogClient() false\n");
            return -1;
        }
    }
    
    LOG_COMM_STU commuStu;


    CLogClient::QueryLogBuildMessage(DEBUG_LEVEL, nLogCount, strKeyWord, "2020-01-09", "2023-11-01", commuStu);

    // Send to Server
    g_clientObj.SendMessageToServer(commuStu);

    std::string strRspData;
    // Get the Response
    if (g_clientObj.GetRespondFromServer(strRspData) == false)
    {
        AddLog(ERROR_LEVEL, "Log Client", "Fail to get response from server");
        return -1;
    }
    // printf("==========================================\n");

    // remove the last character, if it is '\n'
    if (strRspData.back() == '\n') 
    {
        strRspData.pop_back();
    }

    strResult = strRspData;

    return 0;
}

int ClearLog()
{

    LOG_COMM_STU commuStu;
    CLogClient::ClrLogBuildMessage(DEBUG_LEVEL, commuStu);

    // Send to Server
    g_clientObj.SendMessageToServer(commuStu);

    std::string strRspData;
    // Get the Response
    if (g_clientObj.GetRespondFromServer(strRspData) == false)
    {
        AddLog(ERROR_LEVEL, "Log Client", "Fail to get response from server");
        return -1;
    }
    return 0;
}


int DeleteLog(int nLogCount)
{

    LOG_COMM_STU commuStu;
    CLogClient::DelLogBuildMessage(DEBUG_LEVEL, nLogCount, commuStu);

    // Send to Server
    g_clientObj.SendMessageToServer(commuStu);

    std::string strRspData;
    // Get the Response
    if (g_clientObj.GetRespondFromServer(strRspData) == false)
    {
        AddLog(ERROR_LEVEL, "Log Client", "Fail to get response from server");
        return -1;
    }
    return 0;
}


bool CLogClient::IsLogClientInit()
{
    return (m_socketFd != -1);
}

CLogClient::CLogClient():m_socketFd(-1)
{
	

}

bool CLogClient::LockMutex()
{
    pthread_mutex_lock(m_pAddLogMutex);

    return true;
}

bool CLogClient::UnLockMutex()
{
    pthread_mutex_unlock(m_pAddLogMutex);

    return true;
}

int CLogClient::InitLogClient()
{
#ifdef USE_TCP_FLAG

    m_socketFd = SocketInitTCPClient();

	struct sockaddr_in ServerInfo;
    memset(&ServerInfo, 0 ,sizeof(ServerInfo));
	
	ServerInfo.sin_family = AF_INET;				//协议
	ServerInfo.sin_port = htons(8888);				//端口号
	ServerInfo.sin_addr.s_addr = inet_addr("127.0.0.1");

	int ret = connect(m_socketFd, (struct sockaddr *)&ServerInfo, sizeof(ServerInfo));
	if(ret == -1)
	{
		printf("connect error, please make sure the server is on first when using TCP protocol\n");
	}

#else
    m_socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    if (m_socketFd < 0) 
    {
        AddLog(FATAL_LEVEL, "Log Client", "Failed to create socket");
        return false;
    }
    
    memset(&m_ServerAddr, 0, sizeof(m_ServerAddr));
    m_ServerAddr.sin_family = AF_INET;
    m_ServerAddr.sin_port = htons(8888); // 端口号
    m_ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP地址

#endif
    m_pAddLogMutex = new pthread_mutex_t();
    pthread_mutex_init(m_pAddLogMutex, NULL);

    return true;
}

CLogClient::~CLogClient()
{
    if (m_pAddLogMutex!=NULL)
    {
        pthread_mutex_destroy(m_pAddLogMutex);
    }
    close(m_socketFd);
}

// [Add Log] Operation, output in parameter "commStu"
bool CLogClient::AddLogBuildMessage(LOG_LEVEL nLogLevel, const std::string &strObj, const std::string &strDetailInfo, LOG_COMM_STU &commStu)
{
    memset(&commStu, 0, sizeof(LOG_COMM_STU));
    commStu.optType = OPT_ADD_LOG;
    
    LOG_INFO_STU logInfoStu;
    memset(&logInfoStu, 0, sizeof(LOG_INFO_STU));

    logInfoStu.type = nLogLevel;
    strncpy(logInfoStu.objectArr, strObj.c_str(), sizeof(logInfoStu.objectArr));
    strncpy(logInfoStu.detailArr, strDetailInfo.c_str(), sizeof(logInfoStu.detailArr));    

    // printf("[%s] logInfoStu.objectArr=%s, logInfoStu.detailArr = %s\n", __func__, logInfoStu.objectArr, logInfoStu.detailArr);


    memcpy(commStu.allDataInfo, (char *)&logInfoStu , sizeof(logInfoStu));

    return true;

}

// [Delete Log] Operation, output in parameter "commStu"
bool CLogClient::DelLogBuildMessage(LOG_LEVEL nLogLevel, int nCount, LOG_COMM_STU &commStu)
{
    memset(&commStu, 0, sizeof(LOG_COMM_STU));
    
    commStu.optType = OPT_DEL_LOG;
    LOG_DEL_STU delStu;
    delStu.type = nLogLevel;
    delStu.nCount = nCount;

    memcpy(commStu.allDataInfo, (char *)&delStu , sizeof(delStu));
    return true;
}

// [Clear Log] Operation, output in parameter "commStu"
bool CLogClient::ClrLogBuildMessage(LOG_LEVEL nLogLevel, LOG_COMM_STU &commStu)
{
    memset(&commStu, 0, sizeof(LOG_COMM_STU));
    
    commStu.optType = OPT_CLEAR_LOG;
    LOG_DEL_STU delStu;
    delStu.type = nLogLevel;

    memcpy(commStu.allDataInfo, (char *)&delStu , sizeof(delStu));
    return true;
}


// [Query Log] Operation, output in parameter "commStu"
bool CLogClient::QueryLogBuildMessage(LOG_LEVEL nLogLevel, int nCount, const std::string &strKeyWord, const std::string &strStartDateTime, const std::string &strEndDateTime, LOG_COMM_STU &commStu)
{
    memset(&commStu, 0, sizeof(LOG_COMM_STU));
    
    commStu.optType = OPT_QUERY_LOG;
    LOG_QUERY_STU queryStu;
    queryStu.type = nLogLevel;
    queryStu.nCount = nCount;

    strncpy(queryStu.keyword, strKeyWord.c_str(), sizeof(queryStu.keyword));
    strncpy(queryStu.startDateTimeArr, strStartDateTime.c_str(), sizeof(queryStu.startDateTimeArr));
    strncpy(queryStu.endDateTimeArr, strEndDateTime.c_str(), sizeof(queryStu.endDateTimeArr));

    memcpy(commStu.allDataInfo, (char *)&queryStu , sizeof(queryStu));
    return true;
}

// use UDP to send message
bool CLogClient::SendMessageToServer(const LOG_COMM_STU &commStu)
{
    char sendBuff[640] = {0};

    // 强制把结构体，转成字符数组来传递
    memcpy(sendBuff, (char *)&commStu , sizeof(LOG_COMM_STU));

#ifdef USE_TCP_FLAG
    int ret = send(m_socketFd, sendBuff, sizeof(sendBuff), 0);
#else
	int ret = sendto(m_socketFd, sendBuff, sizeof(sendBuff), 0, (struct sockaddr*)&m_ServerAddr, sizeof(m_ServerAddr));
#endif


    if (ret < 0)
    {
        printf("sendto fail ret = %d\n", ret);
        return false;
    }
    
    return true;
}


bool CLogClient::GetRespondFromServer(std::string &strRspData)
{
    socklen_t clientAddrLen = sizeof(struct sockaddr);

    // If too large maybe should recieve many time
    std::string strRecvData;

#ifdef USE_TCP_FLAG
    int ret = RecvDataByTCP(m_socketFd, strRecvData, 0);
    if (ret < 0)
    {
        printf("Client Error receiving data\n");
        return false;
    }

#else
    int ret = RecvDataByUDP(m_socketFd, strRecvData, 0, NULL, &clientAddrLen);
    if (ret < 0)
    {
        printf("Client Error receiving data\n");
        return false;
    }

#endif

    strRspData = strRecvData;

    return true;
}